"""
书法评价模块
针对不同书法风格，提供专业且详细的评价
"""
import os
import json
import numpy as np
import logging
import traceback
from datetime import datetime
import math
from scipy.spatial.distance import cosine
import time

# 导入风格模型
from style_models import (
    get_style_model, 
    STYLE_REGULAR, 
    STYLE_RUNNING,
    STYLE_CURSIVE,
    STYLE_CLERICAL,
    STYLE_SEAL,
    SUPPORTED_STYLES,
    STYLE_NAMES
)

# 配置日志
logger = logging.getLogger(__name__)

class CalligraphyEvaluator:
    """书法评价器"""
    def __init__(self, style_name=STYLE_REGULAR, model_path=None):
        # 加载指定风格的模型
        self.style_name = style_name
        self.style_model = get_style_model(style_name, model_path)
        
        # 从风格模型中获取评价参数
        self.feature_weights = self.style_model.feature_weights
        self.ideal_features = self.style_model.ideal_features
        self.evaluation_dimensions = self.style_model.evaluation_dimensions
        
        # 加载建议模板
        self.suggestion_templates = self._load_suggestion_templates()
        
        # 用户反馈记录
        self.feedback_history = []
        
        # 初始化评价ID计数器
        self.evaluation_counter = 0
        
        logger.info(f"初始化了{style_name}风格的评价器")
    
    def _load_model(self, model_path):
        """加载理想特征模型，如果不存在则使用默认值"""
        if model_path and os.path.exists(model_path):
            with open(model_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        else:
            # 使用默认的理想特征值
            return {
                'grid_density': [0.15, 0.22, 0.16, 0.22, 0.30, 0.22, 0.16, 0.22, 0.15],
                'center_offset': [0.0, 0.0],
                'component_distribution': [5, 150, 30, 400, 3.5],
                'curvature': [1.2, 0.4, 2.1, 0.3, 1.8],
                'angle_distribution': [0.12, 0.15, 0.12, 0.13, 0.12, 0.12, 0.12, 0.12],
                'line_width_variance': [4.5, 1.2, 0.3],
                'wavelet_energy': [0.4, 0.2, 0.15, 0.1, 0.05, 0.04, 0.03, 0.03],
                'fourier_descriptors': [1.0] + [0.2] * 19,  # 简化的傅里叶描述子
                'structural_complexity': [0.3, 0.25, 1.65]
            }
    
    def _load_suggestion_templates(self):
        """加载建议模板库"""
        return {
            '笔法': {
                'high': [
                    "您的笔法流畅有力，尤其是{specific_aspect}的处理显示出较高的控制力。",
                    "笔画运行自如，显示出良好的节奏感，{specific_aspect}非常出色。",
                    "笔法娴熟，线条的收放自如，尤其是{specific_aspect}处理得当。"
                ],
                'medium': [
                    "笔法基本掌握，但{specific_aspect}的控制还有提升空间。建议练习{practice_suggestion}。",
                    "线条流畅度适中，{specific_aspect}需要更多练习。尝试{practice_suggestion}。",
                    "笔画运行基本流畅，但{specific_aspect}还不够稳定。可以通过{practice_suggestion}来改进。"
                ],
                'low': [
                    "笔法尚需加强，{specific_aspect}显得不够熟练。建议重点练习{practice_suggestion}。",
                    "线条控制力有待提高，尤其是{specific_aspect}。可以尝试{practice_suggestion}来加强基本功。",
                    "笔画运行不够流畅，{specific_aspect}需要更多基础训练。建议练习{practice_suggestion}。"
                ]
            },
            '结构': {
                'high': [
                    "字形结构严谨，{specific_aspect}处理得当，比例协调。",
                    "结构布局合理，{specific_aspect}安排有序，显示出良好的空间把握能力。",
                    "部件之间关系处理得当，{specific_aspect}尤为出色，整体结构稳固。"
                ],
                'medium': [
                    "结构基本合理，但{specific_aspect}的处理还可以更精细。建议注意{practice_suggestion}。",
                    "字形布局基本平衡，{specific_aspect}需要调整。可以尝试{practice_suggestion}。",
                    "部件比例大致协调，但{specific_aspect}有改进空间。建议练习{practice_suggestion}。"
                ],
                'low': [
                    "结构布局不够稳定，{specific_aspect}比例失调。建议重点练习{practice_suggestion}。",
                    "部件关系处理不够合理，{specific_aspect}需要改进。可以通过{practice_suggestion}来加强。",
                    "结构不够严谨，尤其是{specific_aspect}。建议练习基本结构的{practice_suggestion}。"
                ]
            },
            '章法': {
                'high': [
                    "整体布局和谐，{specific_aspect}节奏感强，显示出良好的章法意识。",
                    "空间分布均衡，{specific_aspect}处理得当，整体感强。",
                    "疏密关系把握得当，{specific_aspect}尤为出色，形成了良好的视觉效果。"
                ],
                'medium': [
                    "整体布局基本合理，但{specific_aspect}的处理还可以改进。建议注意{practice_suggestion}。",
                    "空间利用基本得当，{specific_aspect}需要调整。可以尝试{practice_suggestion}。",
                    "疏密关系基本协调，但{specific_aspect}有提升空间。建议练习{practice_suggestion}。"
                ],
                'low': [
                    "整体布局不够协调，{specific_aspect}问题明显。建议重点练习{practice_suggestion}。",
                    "空间分布失衡，{specific_aspect}需要改进。可以通过{practice_suggestion}来加强。",
                    "疏密关系处理不当，尤其是{specific_aspect}。建议练习基本章法的{practice_suggestion}。"
                ]
            }
        }
    
    def _get_specific_aspects(self, dimension):
        """获取特定维度的具体表现方面"""
        aspects = {
            '笔法': ['起笔与收笔', '转折处理', '线条粗细变化', '笔画流畅度', '运笔节奏'],
            '结构': ['部件比例', '字体重心', '部件间关系', '空间布局', '结构平衡性'],
            '章法': ['整体布局', '疏密关系', '行气连贯性', '风格统一性', '空间利用']
        }
        return aspects.get(dimension, [])
    
    def _get_practice_suggestions(self, dimension, issue_index):
        """获取针对性的练习建议"""
        suggestions = {
            '笔法': [
                "临摹基本笔画，尤其注重起笔和收笔的力度控制",
                "练习笔画的转折技巧，注意转折处的停顿和提按",
                "练习使用不同力度控制线条粗细变化",
                "慢速练习基本笔画，逐渐提速，保持流畅度",
                "通过有节奏的点画练习，培养笔画节奏感"
            ],
            '结构': [
                "临摹标准字帖，注重部件大小比例的掌握",
                "练习字体重心的把控，可以使用九宫格辅助",
                "分解复杂字形，练习部件组合关系",
                "使用方格纸练习空间布局意识",
                "对称字练习，加强结构平衡感"
            ],
            '章法': [
                "临摹整页作品，注意整体布局节奏",
                "练习疏密变化的对比效果",
                "连续书写练习，加强行气连贯性",
                "同一风格多字练习，保持风格一致性",
                "在不同尺寸纸张上练习，提高空间利用能力"
            ]
        }
        
        # 将字符串级别转换为索引
        if isinstance(issue_index, str):
            if issue_index == "high":
                index = 0  # 高水平，给出基础建议
            elif issue_index == "medium":
                index = 2  # 中等水平，给出中级建议
            else:  # "low"
                index = 4  # 低水平，给出进阶建议
        else:
            # 如果已经是数字索引，直接使用
            index = issue_index
        
        # 确保索引在有效范围内
        index = min(index, len(suggestions.get(dimension, [])) - 1)
        index = max(0, index)  # 确保索引不小于0
        
        return suggestions.get(dimension, ["练习基本功，提高整体水平"])[index]
    
    def evaluate_features(self, features):
        """评估特征并计算相似度得分"""
        try:
            logger.info("开始评估特征...")
            
            if not features:
                logger.warning("没有提供要评估的特征")
                return {}, {}
            
            # 初始化分数字典
            feature_scores = {}
            
            # 计算每个特征的得分
            for feature_name, feature_value in features.items():
                try:
                    # 跳过空值
                    if feature_value is None:
                        logger.warning(f"特征 {feature_name} 为空值，跳过评估")
                        feature_scores[feature_name] = 0.0
                        continue
                    
                    # 获取理想特征值
                    ideal_value = self.ideal_features.get(feature_name)
                    
                    if ideal_value is None:
                        logger.warning(f"特征 {feature_name} 没有理想值定义，跳过评估")
                        feature_scores[feature_name] = 0.0
                        continue
                    
                    # 记录特征值类型和内容
                    logger.debug(f"特征 {feature_name}:")
                    
                    # 记录实际值信息
                    if isinstance(feature_value, list):
                        logger.debug(f"  实际值类型: {type(feature_value)}, 长度: {len(feature_value)}, "
                                    f"元素类型: {type(feature_value[0]) if feature_value else 'empty'}")
                    elif isinstance(feature_value, tuple):
                        logger.debug(f"  实际值类型: {type(feature_value)}, 值: {feature_value}")
                    elif hasattr(feature_value, 'shape'):
                        logger.debug(f"  实际值类型: {type(feature_value)}, 形状: {feature_value.shape}")
                    else:
                        logger.debug(f"  实际值类型: {type(feature_value)}, 值: {feature_value}")
                    
                    # 记录理想值信息
                    if isinstance(ideal_value, list):
                        logger.debug(f"  理想值类型: {type(ideal_value)}, 长度: {len(ideal_value)}, "
                                    f"元素类型: {type(ideal_value[0]) if ideal_value else 'empty'}")
                    elif isinstance(ideal_value, tuple):
                        logger.debug(f"  理想值类型: {type(ideal_value)}, 值: {ideal_value}")
                    elif hasattr(ideal_value, 'shape'):
                        logger.debug(f"  理想值类型: {type(ideal_value)}, 形状: {ideal_value.shape}")
                    else:
                        logger.debug(f"  理想值类型: {type(ideal_value)}, 值: {ideal_value}")
                    
                    # 根据特征类型计算相似度
                    if isinstance(feature_value, (list, tuple)) and isinstance(ideal_value, (list, tuple)):
                        # 处理列表类型特征
                        # 确保长度一致
                        if len(feature_value) != len(ideal_value):
                            logger.warning(f"特征 {feature_name} 实际值和理想值长度不一致，将截断到最短长度")
                            min_len = min(len(feature_value), len(ideal_value))
                            feature_value = feature_value[:min_len]
                            ideal_value = ideal_value[:min_len]
                        
                        # 计算余弦相似度
                        logger.debug(f"  计算特征 {feature_name} 的余弦相似度")
                        similarity = self._calculate_similarity(feature_value, ideal_value)
                        feature_scores[feature_name] = float(similarity)
                        logger.debug(f"  得分: {feature_scores[feature_name]}")
                    elif feature_name == 'center_offset' and (isinstance(feature_value, (list, tuple)) and isinstance(ideal_value, (list, tuple))):
                        # 特殊处理center_offset（二维坐标）
                        logger.debug(f"  计算特征 {feature_name} 的欧氏距离")
                        
                        # 确保转换为列表
                        f_value = list(feature_value) if isinstance(feature_value, tuple) else feature_value
                        i_value = list(ideal_value) if isinstance(ideal_value, tuple) else ideal_value
                        
                        # 计算欧氏距离
                        squared_diff = sum((float(f) - float(i)) ** 2 for f, i in zip(f_value, i_value))
                        euclidean_dist = math.sqrt(squared_diff)
                        
                        # 使用高斯核将距离转换为相似度（0-1范围）
                        similarity = float(np.exp(-0.5 * (euclidean_dist / 0.5) ** 2))
                        feature_scores[feature_name] = float(similarity)
                        logger.debug(f"  得分: {feature_scores[feature_name]}")
                    
                    # 处理新增的整篇章法特征
                    elif feature_name == 'visual_balance':
                        # 视觉平衡度特征使用高斯距离度量
                        logger.debug(f"  计算整篇章法特征 {feature_name} 的高斯距离")
                        f_value = float(feature_value)
                        i_value = float(ideal_value)
                        squared_diff = (f_value - i_value) ** 2
                        similarity = float(np.exp(-0.5 * (squared_diff / 0.1) ** 2))  # sigma=0.1
                        feature_scores[feature_name] = float(similarity)
                        logger.debug(f"  得分: {feature_scores[feature_name]}")
                    
                    elif feature_name == 'spacing_consistency':
                        # 行列间距一致性特征，使用加权平均
                        logger.debug(f"  计算整篇章法特征 {feature_name} 的加权平均相似度")
                        # 确保转换为列表
                        f_value = list(feature_value) if isinstance(feature_value, tuple) else feature_value
                        i_value = list(ideal_value) if isinstance(ideal_value, tuple) else ideal_value
                        
                        # 确保长度一致
                        min_len = min(len(f_value), len(i_value))
                        f_value = f_value[:min_len]
                        i_value = i_value[:min_len]
                        
                        # 计算行距和列距一致性得分（各占50%）
                        scores = []
                        for idx in range(min_len):
                            squared_diff = (float(f_value[idx]) - float(i_value[idx])) ** 2
                            sim = float(np.exp(-0.5 * (squared_diff / 0.1) ** 2))
                            scores.append(sim)
                        
                        # 计算总得分
                        similarity = sum(scores) / len(scores) if scores else 0.0
                        feature_scores[feature_name] = float(similarity)
                        logger.debug(f"  得分: {feature_scores[feature_name]}")
                    
                    elif feature_name == 'space_utilization':
                        # 空间利用率特征使用高斯距离度量
                        logger.debug(f"  计算整篇章法特征 {feature_name} 的高斯距离")
                        f_value = float(feature_value)
                        i_value = float(ideal_value)
                        squared_diff = (f_value - i_value) ** 2
                        similarity = float(np.exp(-0.5 * (squared_diff / 0.1) ** 2))
                        feature_scores[feature_name] = float(similarity)
                        logger.debug(f"  得分: {feature_scores[feature_name]}")
                    
                    elif feature_name == 'margin_ratio':
                        # 边缘留白比例特征，比较四个方向的留白
                        logger.debug(f"  计算整篇章法特征 {feature_name} 的欧氏距离相似度")
                        # 确保转换为列表
                        f_value = list(feature_value) if isinstance(feature_value, tuple) else feature_value
                        i_value = list(ideal_value) if isinstance(ideal_value, tuple) else ideal_value
                        
                        # 确保长度一致
                        min_len = min(len(f_value), len(i_value))
                        f_value = f_value[:min_len]
                        i_value = i_value[:min_len]
                        
                        # 计算欧氏距离
                        squared_diff = sum((float(f) - float(i)) ** 2 for f, i in zip(f_value, i_value))
                        euclidean_dist = math.sqrt(squared_diff)
                        
                        # 使用高斯核将距离转换为相似度
                        similarity = float(np.exp(-0.5 * (euclidean_dist / 0.1) ** 2))
                        feature_scores[feature_name] = float(similarity)
                        logger.debug(f"  得分: {feature_scores[feature_name]}")
                    
                    else:
                        # 处理标量特征
                        # 使用高斯距离度量，接近理想值得分高
                        logger.debug(f"  计算特征 {feature_name} 的高斯距离")
                        
                        # 尝试转换为浮点数
                        try:
                            if isinstance(feature_value, (list, tuple)):
                                # 如果是列表或元组，但理想值是标量，则使用平均值
                                logger.warning(f"特征 {feature_name} 是列表/元组，但理想值是标量，使用平均值")
                                feature_value_float = sum(float(x) for x in feature_value) / len(feature_value)
                            else:
                                feature_value_float = float(feature_value)
                                
                            if isinstance(ideal_value, (list, tuple)):
                                # 如果是列表或元组，但实际值是标量，则使用平均值
                                logger.warning(f"特征 {feature_name} 理想值是列表/元组，但实际值是标量，使用平均值")
                                ideal_value_float = sum(float(x) for x in ideal_value) / len(ideal_value)
                            else:
                                ideal_value_float = float(ideal_value)
                            
                            diff = abs(feature_value_float - ideal_value_float)
                            max_val = max(abs(ideal_value_float), 1.0)
                            similarity = float(np.exp(-0.5 * (diff / max_val) ** 2))
                            feature_scores[feature_name] = float(similarity)
                            logger.debug(f"  得分: {feature_scores[feature_name]}")
                        except ValueError as ve:
                            logger.error(f"无法将特征 {feature_name} 转换为浮点数: {str(ve)}")
                            feature_scores[feature_name] = 0.0
                
                except Exception as e:
                    logger.error(f"处理特征 {feature_name} 时出错: {str(e)}")
                    logger.error(traceback.format_exc())
                    feature_scores[feature_name] = 0.0
            
            # 计算各维度的得分
            dimension_scores = {}
            for dimension, feature_list in self.evaluation_dimensions.items():
                logger.debug(f"计算维度 {dimension} 的得分")
                
                # 计算每个维度的加权平均分数
                total_weight = 0.0
                weighted_score = 0.0
                
                for feature in feature_list:
                    if feature in feature_scores and feature in self.feature_weights:
                        weight = float(self.feature_weights[feature])
                        score = float(feature_scores[feature])
                        weighted_score += weight * score
                        total_weight += weight
                
                # 计算维度最终得分
                if total_weight > 0:
                    dimension_scores[dimension] = float(weighted_score / total_weight)
                else:
                    dimension_scores[dimension] = 0.0
                    
                logger.debug(f"  维度 {dimension} 得分: {dimension_scores[dimension]}")
            
            # 计算所有维度的平均分数作为总分（转换为0-100分制）
            dimension_score_list = list(dimension_scores.values())
            logger.debug(f"维度得分列表: {dimension_score_list}")
            
            if dimension_score_list:
                total_score = sum(dimension_score_list) * 100 / len(dimension_score_list)
            else:
                total_score = 0.0
                
            logger.info(f"总得分: {total_score}")
            
            # 返回评估结果
            return {
                'total_score': float(total_score),
                'dimension_scores': {k: float(v) for k, v in dimension_scores.items()},
                'feature_scores': feature_scores
            }
        except Exception as e:
            logger.error(f"评估特征时出错: {str(e)}")
            logger.error(traceback.format_exc())
            return {}, {}
    
    def _calculate_similarity(self, v1, v2):
        """计算两个向量之间的相似度（使用余弦相似度）"""
        try:
            logger.debug("开始计算余弦相似度")
            
            # 记录输入向量的类型和形状
            logger.debug(f"向量1类型: {type(v1)}")
            logger.debug(f"向量2类型: {type(v2)}")
            
            # 转换为NumPy数组
            v1 = np.array(v1, dtype=np.float64)
            v2 = np.array(v2, dtype=np.float64)
            
            logger.debug(f"转换后向量1形状: {v1.shape}, 类型: {v1.dtype}")
            logger.debug(f"转换后向量2形状: {v2.shape}, 类型: {v2.dtype}")
            
            # 如果向量为零，则相似度为0
            sum_v1 = np.sum(np.abs(v1))
            sum_v2 = np.sum(np.abs(v2))
            logger.debug(f"向量1绝对值之和: {sum_v1}")
            logger.debug(f"向量2绝对值之和: {sum_v2}")
            
            if sum_v1 < 1e-10 or sum_v2 < 1e-10:
                logger.debug("检测到零向量，返回0.0")
                return 0.0
            
            # 计算余弦相似度
            dot_product = float(np.dot(v1, v2))
            norm_v1 = float(np.linalg.norm(v1))
            norm_v2 = float(np.linalg.norm(v2))
            
            logger.debug(f"点积: {dot_product}")
            logger.debug(f"向量1范数: {norm_v1}")
            logger.debug(f"向量2范数: {norm_v2}")
            
            if norm_v1 > 0 and norm_v2 > 0:
                cos_sim = float(dot_product / (norm_v1 * norm_v2))
                # 确保结果在[0,1]范围内
                cos_sim = float(max(0.0, min(1.0, (cos_sim + 1.0) / 2.0)))
                logger.debug(f"余弦相似度: {cos_sim}")
                return cos_sim
            else:
                logger.debug("向量范数为零，返回0.0")
                return 0.0
        except Exception as e:
            logger.error(f"计算余弦相似度时出错: {str(e)}")
            logger.error(traceback.format_exc())
            return 0.0
    
    def generate_suggestions(self, evaluation_results):
        """
        根据评估结果生成针对性建议
        evaluation_results包含:
        - dimension_scores: 各维度得分
        - feature_scores: 各特征得分
        - style_fitness: 风格契合度
        """
        suggestions = {}
        dimension_scores = evaluation_results['dimension_scores']
        feature_scores = evaluation_results['feature_scores']
        style_fitness = evaluation_results.get('style_fitness', 1.0)
        
        # 对每个维度生成建议
        for dimension, score in dimension_scores.items():
            # 确定表现级别: 高、中、低
            if score >= 85:
                level = "high"
            elif score >= 70:
                level = "medium"
            else:
                level = "low"
            
            # 获取该维度涉及的特征
            dimension_features = []
            if dimension == '笔法':
                dimension_features = ['line_width_variance', 'angle_distribution', 'curvature']
            elif dimension == '结构':
                dimension_features = ['grid_density', 'center_offset', 'component_distribution']
            elif dimension == '章法':
                dimension_features = ['fourier_descriptors', 'structural_complexity', 'wavelet_energy',
                                      'visual_balance', 'spacing_consistency', 'space_utilization', 'margin_ratio']
            
            # 找出该维度下得分最低的特征
            min_score = float('inf')
            weakest_feature = None
            
            for feature in dimension_features:
                if feature in feature_scores and feature_scores[feature] < min_score:
                    min_score = feature_scores[feature]
                    weakest_feature = feature
            
            # 生成针对最弱特征的具体建议
            specific_suggestion = ""
            if weakest_feature:
                # 检查是否有针对该风格的特定建议
                style_suggestion = self.style_model.get_style_specific_suggestions(weakest_feature, level)
                
                if style_suggestion:
                    specific_suggestion = style_suggestion
                else:
                    # 如果没有特定风格建议，使用通用模板
                    if weakest_feature == 'grid_density':
                        specific_suggestion = "笔画分布不够均衡，建议关注字的整体布局，使各部分比例协调。"
                    elif weakest_feature == 'center_offset':
                        specific_suggestion = "字的重心偏离中心，建议练习掌握字的结构中心，使整体平衡。"
                    elif weakest_feature == 'component_distribution':
                        specific_suggestion = "部件分布不够合理，建议研究字的部件关系，调整布局使整体协调。"
                    elif weakest_feature == 'curvature':
                        specific_suggestion = "笔画曲直变化不够自然，建议练习运笔的起伏转折，增强笔画的韵律感。"
                    elif weakest_feature == 'angle_distribution':
                        specific_suggestion = "笔画角度分布不够丰富，建议关注笔画的方向变化，增强字的动感。"
                    elif weakest_feature == 'line_width_variance':
                        specific_suggestion = "笔画粗细变化不够明显，建议练习用笔轻重变化，增强笔画的力度对比。"
                    elif weakest_feature == 'wavelet_energy':
                        specific_suggestion = "笔画能量分布不够均衡，建议关注笔画的节奏感和韵律，使字更有生命力。"
                    elif weakest_feature == 'fourier_descriptors':
                        specific_suggestion = "整体轮廓特征不够鲜明，建议研究字形的特点，增强识别性和美感。"
                    elif weakest_feature == 'structural_complexity':
                        specific_suggestion = "结构复杂度不合适，建议掌握简繁得当的原则，使字既清晰又有韵味。"
                    # 整篇章法特征的建议
                    elif weakest_feature == 'visual_balance':
                        specific_suggestion = self._get_visual_balance_suggestions(min_score)
                    elif weakest_feature == 'spacing_consistency':
                        specific_suggestion = self._get_spacing_consistency_suggestions(min_score)
                    elif weakest_feature == 'space_utilization':
                        specific_suggestion = self._get_space_utilization_suggestions(min_score)
                    elif weakest_feature == 'margin_ratio':
                        specific_suggestion = self._get_margin_ratio_suggestions(min_score)
                    else:
                        specific_suggestion = "此特征有提升空间，建议参考优秀范本进行有针对性的练习。"
            
            # 根据维度选择练习方式建议
            practice_suggestion = self._get_practice_suggestions(dimension, level)
            
            # 组合最终建议
            suggestions[dimension] = {
                'score': round(float(score), 2) if isinstance(score, (int, float)) else score,
                'level': level,
                'suggestion': specific_suggestion + " " + practice_suggestion
            }
        
        # 添加风格契合度建议
        if style_fitness < 0.85:
            current_style_name = STYLE_NAMES.get(self.style_name, self.style_name)
            suggestions['风格契合度'] = {
                'score': round(style_fitness * 100, 2),
                'level': 'low' if style_fitness < 0.8 else 'medium',
                'suggestion': f"作品与{current_style_name}风格存在一定差异，建议参考{current_style_name}的典型范例，注意其特有的笔法、结构和章法特点，逐步调整自己的书写习惯以贴近{current_style_name}风格。"
            }
        
        return suggestions
    
    def evaluate_calligraphy(self, features):
        """
        综合评价书法作品
        返回总体评价结果
        """
        logger.info(f"开始对书法进行综合评价，风格：{self.style_name}")
        start_time = time.time()

        # 特征评价
        feature_scores = self.evaluate_features(features)
        logger.debug(f"特征评分计算耗时: {time.time() - start_time:.3f}秒")
        
        # 计算各维度得分
        dimension_scores = {}
        
        # 笔法分数
        dimension_scores['笔法'] = self._calculate_dimension_score(
            feature_scores, 
            ['line_width_variance', 'angle_distribution', 'curvature']
        )
        
        # 结构分数
        dimension_scores['结构'] = self._calculate_dimension_score(
            feature_scores, 
            ['grid_density', 'center_offset', 'component_distribution']
        )
        
        # 章法分数（包含整篇章法特征）
        dimension_scores['章法'] = self._calculate_dimension_score(
            feature_scores, 
            ['fourier_descriptors', 'structural_complexity', 'wavelet_energy',
             'visual_balance', 'spacing_consistency', 'space_utilization', 'margin_ratio']
        )
        
        logger.debug(f"维度评分: {dimension_scores}")
        logger.debug(f"维度评分计算耗时: {time.time() - start_time:.3f}秒")
        
        # 计算总分 (1-100分)
        total_score = 0
        for dimension, score in dimension_scores.items():
            total_score += score * self.style_model.dimension_weights.get(dimension, 1/3)
        
        # 风格契合度评分（新增）
        style_fitness_factor = self._calculate_style_fitness(features)
        logger.info(f"风格契合度因子: {style_fitness_factor}")
        logger.debug(f"风格契合度计算耗时: {time.time() - start_time:.3f}秒")
        
        # 根据风格契合度调整总分
        original_score = total_score
        total_score = total_score * style_fitness_factor
        
        if style_fitness_factor < 0.95:
            logger.info(f"作品风格与{self.style_name}不太匹配，分数从{original_score:.2f}调整为{total_score:.2f}")
        
        # 生成评价建议
        suggestions = self.generate_suggestions({
            'dimension_scores': dimension_scores,
            'feature_scores': feature_scores,
            'style_fitness': style_fitness_factor
        })
        
        # 根据总分生成总体评价
        overall_comment = self._generate_overall_comment(total_score, style_fitness_factor)
        
        # 构建评价结果
        evaluation_result = {
            'style_name': STYLE_NAMES.get(self.style_name, self.style_name),
            'total_score': round(total_score, 1),
            'dimension_scores': {k: round(float(v), 2) if isinstance(v, (int, float)) else v for k, v in dimension_scores.items()},
            'feature_scores': {k: round(float(v), 2) if isinstance(v, (int, float)) else v for k, v in feature_scores.items()},
            'overall_comment': overall_comment,
            'suggestions': suggestions,
            'evaluation_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'style_fitness': round(style_fitness_factor, 2)
        }
        
        total_eval_time = time.time() - start_time
        logger.info(f"评价完成，总分: {total_score:.2f}，评价耗时: {total_eval_time:.3f}秒")
        return evaluation_result
    
    def _calculate_style_fitness(self, features):
        """
        计算书法作品与当前风格的契合度
        返回一个0-1的因子，用于调整总分
        """
        start_time = time.time()
        
        # 选择最能体现风格差异的特征进行比较
        key_style_features = [
            'angle_distribution',    # 角度分布体现笔法特点
            'curvature',             # 曲率体现笔法特点
            'line_width_variance',   # 线宽变化体现笔法特点
            'fourier_descriptors',   # 傅里叶描述符体现整体风格
            'structural_complexity'  # 结构复杂度体现风格特点
        ]
        
        # 计算关键特征的平均相似度
        similarity_scores = []
        
        # 限制特征比较的数量，提高效率
        valid_features = [f for f in key_style_features if f in features and f in self.style_model.ideal_features]
        
        # 如果有效特征太少，直接返回默认值
        if len(valid_features) < 2:
            logger.warning(f"有效风格特征数量不足: {len(valid_features)}，使用默认契合度0.9")
            return 0.9
            
        # 最多使用3个特征进行比较，减少计算量
        valid_features = valid_features[:3]
        
        for feature_name in valid_features:
            actual = features[feature_name]
            ideal = self.style_model.ideal_features[feature_name]
            
            # 确保可以比较
            if isinstance(actual, list) and isinstance(ideal, list):
                # 长度可能不同，取最小长度
                min_len = min(len(actual), len(ideal))
                if min_len > 0:
                    actual = actual[:min_len]
                    ideal = ideal[:min_len]
                    sim = 1.0 - self._calculate_similarity(actual, ideal)
                    similarity_scores.append(sim)
            elif not isinstance(actual, list) and not isinstance(ideal, list):
                # 单值特征
                diff = abs(actual - ideal) / max(abs(ideal), 1e-10)
                sim = max(0, 1.0 - diff)
                similarity_scores.append(sim)
        
        if not similarity_scores:
            return 1.0  # 默认完全契合
            
        # 计算平均契合度
        avg_similarity = sum(similarity_scores) / len(similarity_scores)
        
        # 使用Sigmoid函数将相似度映射到一个合理的调整因子范围(0.7-1.0)
        # 非常不匹配的作品最多降低30%的分数
        adjustment_factor = 0.7 + 0.3 / (1 + math.exp(-10 * (avg_similarity - 0.5)))
        
        logger.debug(f"风格契合度原始相似度: {avg_similarity}, 调整因子: {adjustment_factor}")
        return adjustment_factor
    
    def _generate_overall_comment(self, score, style_fitness):
        """根据总分和风格契合度生成总体评价"""
        # 基础评价
        if score >= 90:
            base_comment = "表现优异，达到专业水准，具有很高的艺术价值。"
        elif score >= 80:
            base_comment = "表现良好，具有扎实的基本功和艺术表现力。"
        elif score >= 70:
            base_comment = "表现中等，基本功尚可，有一定的艺术表现力。"
        elif score >= 60:
            base_comment = "表现一般，基本功有待提高，艺术表现力有限。"
        else:
            base_comment = "有待提高，建议加强基本功训练，提升艺术表现力。"
        
        # 风格契合度评价
        if style_fitness < 0.8:
            style_comment = f"此作品风格特征与{STYLE_NAMES.get(self.style_name, self.style_name)}存在较大差异，建议参考{STYLE_NAMES.get(self.style_name, self.style_name)}的范本进行练习。"
            return base_comment + " " + style_comment
        
        return base_comment
    
    def update_weights(self, user_feedback):
        """
        根据用户反馈更新特征权重
        """
        if not user_feedback:
            return
        
        # 简化的在线学习算法，根据用户反馈调整权重
        learning_rate = 0.05
        
        for feature, feedback_value in user_feedback.items():
            if feature in self.feature_weights:
                # 将反馈值转换为[-1, 1]范围
                delta = float(feedback_value - 0.5)  # 假设反馈在[0, 1]范围内
                
                # 更新权重
                current_weight = float(self.feature_weights[feature])
                self.feature_weights[feature] = float(current_weight + learning_rate * delta)
        
        # 重新归一化权重，确保总和为1
        weight_sum = float(sum(self.feature_weights.values()))
        if weight_sum > 0:
            for feature in self.feature_weights:
                current_weight = float(self.feature_weights[feature])
                self.feature_weights[feature] = float(current_weight / weight_sum)
    
    def change_style(self, style_name):
        """切换到不同的书法风格模型"""
        logging.info(f"正在尝试切换风格到: {style_name}")
        
        if style_name in SUPPORTED_STYLES:
            try:
                # 创建新的风格模型
                logging.info(f"创建新的风格模型: {style_name}")
                self.style_model = get_style_model(style_name)
                logging.info(f"风格模型创建成功: {style_name}, 类型: {type(self.style_model).__name__}")
                
                # 更新评估器的参数
                self.feature_weights = self.style_model.feature_weights
                logging.info(f"更新了特征权重, 包含 {len(self.feature_weights)} 个特征")
                
                self.ideal_features = self.style_model.ideal_features
                logging.info(f"更新了理想特征, 包含 {len(self.ideal_features)} 个特征")
                
                self.evaluation_dimensions = self.style_model.evaluation_dimensions
                logging.info(f"更新了评价维度, 包含 {len(self.evaluation_dimensions)} 个维度")
                
                # 更新风格名称
                self.style_name = style_name
                logging.info(f"成功切换到风格: {style_name}")
                
                return True
            except Exception as e:
                logging.error(f"切换风格时出错: {str(e)}")
                logging.error(traceback.format_exc())
                return False
        else:
            logging.warning(f"不支持的风格: {style_name}")
            return False
    
    @staticmethod
    def get_supported_styles():
        """获取支持的所有书法风格"""
        return [(style, STYLE_NAMES.get(style, style)) for style in SUPPORTED_STYLES]
    
    # 整篇章法特征建议方法
    def _get_visual_balance_suggestions(self, score):
        """为视觉平衡度生成建议"""
        if score >= 0.8:
            return "视觉平衡度良好，整体布局均衡。可以尝试在局部细节上进一步提升平衡感，注意作品的重心分布。"
        elif score >= 0.6:
            return "视觉平衡度尚可，但整体布局有些偏重。建议重点关注作品的重心分布，确保墨迹在画面中形成平衡的视觉效果，可以适当调整字的大小和位置来改善平衡感。"
        else:
            return "视觉平衡度较差，画面重心失衡。建议重新布局，确保墨迹在画面中分布均匀，避免某一区域过于集中或稀疏。可以参考九宫格法则，在关键位置安排重要元素。"

    def _get_spacing_consistency_suggestions(self, score):
        """为行列间距一致性生成建议"""
        if score >= 0.8:
            return "行列间距一致性良好，整体排布规整。可以在保持当前一致性的基础上，尝试微调某些区域的间距，以增强作品的韵律感。"
        elif score >= 0.6:
            return "行列间距有一定的不规则性，影响整体的秩序感。建议练习等距离书写，可以借助九宫格或田字格辅助，保持行与行、列与列之间的间距均匀，增强作品的规整性。"
        else:
            return "行列间距变化较大，缺乏一致性和规律性。建议重点练习字距和行距的掌控，可以使用辅助线或格子进行练习，确保作品中字与字之间、行与行之间的间距保持一致或有规律的变化。"

    def _get_space_utilization_suggestions(self, score):
        """为空间利用率生成建议"""
        if score >= 0.8:
            return "空间利用率良好，作品布局充实而不拥挤。可以尝试在保持整体布局的基础上，通过微调某些区域的密度，增强作品的节奏感。"
        elif score >= 0.6:
            return "空间利用率一般，作品可能过于稀疏或局部过于密集。建议注意作品各部分的密度平衡，避免出现大面积空白或过于拥挤的区域，确保墨与白的比例协调，增强作品的视觉张力。"
        else:
            return "空间利用率较差，作品布局失衡。建议重新规划整体布局，确保墨迹与留白适度，避免过于拥挤或过于空旷的极端情况。可以参考优秀作品中墨与白的比例关系，练习不同密度的布局效果。"

    def _get_margin_ratio_suggestions(self, score):
        """为边缘留白比例生成建议"""
        if score >= 0.8:
            return "边缘留白比例适当，画面呼吸感良好。可以尝试在保持当前留白比例的基础上，微调某些边缘的留白，增强作品的层次感。"
        elif score >= 0.6:
            return "边缘留白比例略有不均，影响整体平衡感。建议适当调整四周留白的比例，特别注意上下左右的留白均衡，避免作品过于偏向某一边缘，给观者适当的视觉呼吸空间。"
        else:
            return "边缘留白比例失调，整体布局受限。建议重点关注作品四周留白的设计，合理安排上下左右的留白空间，避免作品顶边缘或贴近某一侧，遵循传统书法中'上松下紧、左宽右窄'的留白原则，或根据作品需要创造有意义的留白。"

    def _calculate_dimension_score(self, feature_scores, features_list):
        """
        计算特定维度的得分（笔法、结构、章法）
        基于该维度下各特征的得分计算
        
        Args:
            feature_scores: 所有特征的得分字典
            features_list: 该维度包含的特征列表
            
        Returns:
            该维度的得分 (0-100)
        """
        # 过滤出该维度的特征得分
        dimension_feature_scores = []
        dimension_feature_weights = []
        
        for feature in features_list:
            if feature in feature_scores and feature in self.feature_weights:
                score = feature_scores[feature]
                weight = self.feature_weights[feature]
                dimension_feature_scores.append(score)
                dimension_feature_weights.append(weight)
        
        # 如果没有有效特征，返回默认分数
        if not dimension_feature_scores:
            logger.warning(f"维度计算中没有找到有效特征: {features_list}")
            return 60.0  # 默认中等分数
            
        # 标准化权重，使其和为1
        total_weight = sum(dimension_feature_weights)
        if total_weight > 0:
            normalized_weights = [w / total_weight for w in dimension_feature_weights]
        else:
            # 如果总权重为0，使用平均权重
            normalized_weights = [1.0 / len(dimension_feature_weights)] * len(dimension_feature_weights)
        
        # 计算加权平均分数 (0-1 范围)
        weighted_score = sum(s * w for s, w in zip(dimension_feature_scores, normalized_weights))
        
        # 将分数映射到0-100范围
        final_score = weighted_score * 100.0
        
        # 记录日志
        logger.debug(f"  维度得分: {final_score}")
        
        return final_score 